﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;


namespace Producer
{
    class ProducerThread
    {
        private int m_Byterate;
        private int m_PacketSize;
        private Socket m_Socket;
        private Thread m_Thread;
        private bool m_ThreadExit;
        private byte[] m_Buffer;

        private int m_ThreadDelay;
        private DateTime m_StartTime;
        private long m_TotalBytesSend;
        private double m_AverageByterate;

        public ProducerThread(int byterate, int packetSizeBytes, Socket socket)
        {
            m_Byterate = byterate;
            m_PacketSize = packetSizeBytes;
            m_Socket = socket;
            m_TotalBytesSend = 0;
            m_ThreadDelay = 1000 / (m_Byterate / m_PacketSize);
            m_Buffer = new byte[m_PacketSize];
            Random rnd = new Random();
            rnd.NextBytes(m_Buffer);
        }
        public ProducerThread(int byterate, int packetSizeBytes)
        {
            m_Byterate = byterate;
            m_PacketSize = packetSizeBytes;
            m_TotalBytesSend = 0;
            m_ThreadDelay = 1000 / (m_Byterate / m_PacketSize);
            m_Buffer = new byte[m_PacketSize];
            Random rnd = new Random();
            rnd.NextBytes(m_Buffer);
        }

        public void Start()
        {
            m_ThreadExit = false;
            m_Thread = new Thread(this.threadFn);
            m_Thread.Start();
        }

        public void Stop()
        {
            m_ThreadExit = true;
            m_Thread.Join();
            Console.WriteLine("Stop exit");
        }

        public void PrintStats()
        {
            Console.WriteLine("\nRequested Byterate     : " + m_Byterate);
            Console.WriteLine("Requested Packet Size : " + m_PacketSize);
            Console.WriteLine("Thread delay          : " + m_ThreadDelay);
            Console.WriteLine("Total bytes sent      : " + m_TotalBytesSend);
            Console.WriteLine("AverageByterate       : " + m_AverageByterate);
        }

        private void threadFn()
        {
            m_StartTime = DateTime.Now;

            while (true)
            {
                if (m_ThreadExit)
                    break;
                
                m_Socket.Send(m_Buffer);
                m_TotalBytesSend += m_PacketSize;
           //     Console.WriteLine("thread" + m_Byterate);
                
                /* delay correction */
                m_AverageByterate = m_TotalBytesSend / (DateTime.Now - m_StartTime).TotalSeconds;

                if (m_AverageByterate < m_Byterate && m_ThreadDelay > 0)
                    m_ThreadDelay--;
                else if (m_AverageByterate > m_Byterate)
                    m_ThreadDelay++;
                
                if(m_ThreadDelay > 0)
                 Thread.Sleep(m_ThreadDelay);
            }
            Console.WriteLine("Producer thread terminated");
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length != 4)
            {
                Console.WriteLine("Usage:");
                Console.WriteLine("Producer.exe <server_ip> <port> <byterate> <packet_size>");
                return;
            }

            string ipAddress = args[0];
            string port = args[1];
            string byterate = args[2];
            string packetSize = args[3];

            Console.WriteLine("IpAddress: " + ipAddress);
            Console.WriteLine("Port     : " + port);
            Console.WriteLine("Byterate : " + byterate);
            Console.WriteLine("PSize    : " + packetSize);

            try
            {
                Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                s.Connect(new IPEndPoint(IPAddress.Parse(ipAddress), int.Parse(port)));
                ProducerThread p = new ProducerThread(int.Parse(byterate), int.Parse(packetSize), s);
//                ProducerThread p = new ProducerThread(int.Parse(byterate), int.Parse(packetSize));

                Console.WriteLine("Press key to start Producer");
                Console.ReadKey();
                p.Start();

                while (true)
                {
                    Console.WriteLine("s - get Producer statistics");
                    Console.WriteLine("q - quit Producer");
                    char ch = Console.ReadKey().KeyChar;
                    if (ch == 's')
                    {
                        p.PrintStats();
                    }
                    if (ch == 'q')
                    {
                        p.Stop();
                        break;
                    }
                }
                s.Disconnect(true);
                s.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            
            Console.ReadKey();
        }
    }
}
